//#include <windows.h>

#include "../../epsilon.h"

#include "../../window.h"
#include "window_x11.h"

// This is a really bad implementation...
Display* dpy = NULL;

bool
eWindowX11::create (const eWindowParameters& parameters, eWindow* window)
{
  initialized = false;

  this->window = window;

  dpy = XOpenDisplay (NULL);
  Assert (dpy != NULL, _T ("Display did not open..."));

  if (! dpy) {
    ::dpy = NULL;
    return false;
  }

  ::dpy = dpy;

  int blackColor = BlackPixel (dpy, DefaultScreen (dpy));
  int whiteColor = WhitePixel (dpy, DefaultScreen (dpy));

  w = XCreateSimpleWindow (dpy, DefaultRootWindow (dpy), 0, 0,
                           200, 100, 0, blackColor,
                           blackColor);

  XMapWindow (dpy, w);

  gc = XCreateGC (dpy, w, 0, NULL);

  XSetForeground (dpy, gc, whiteColor);

  XSelectInput (dpy, w, ResizeRedirectMask |
                        ButtonPressMask | ButtonReleaseMask |
		       ButtonMotionMask |
		       KeyPressMask | KeyReleaseMask |
		       PointerMotionMask |
		       FocusChangeMask |
		       EnterWindowMask | LeaveWindowMask |
		       StructureNotifyMask
		       );

#if 0
  const eWindowParametersWin32& parameters_win32 =
      (const eWindowParametersWin32&) parameters;

  if (parameters_win32.instance == NULL)
    hInstance = GetModuleHandle (NULL);
  else
    hInstance = parameters_win32.instance;

  WindowProc = parameters_win32.window_proc;
#endif

  initialized = true;

  update_rect ();

  return true;
}

bool
eWindowX11::destroy (void)
{
  initialized = false;

  // TODO
  return true;
}




void
eWindowX11::Think (void)
{
  XEvent event; // X Event Structure

  while (true) // Run until no events are queued
  {
    // Is There An Event Queued?
    if (XEventsQueued (::dpy, QueuedAfterFlush)) {
      XNextEvent (::dpy, &event);

      switch (event.type) {
        case ButtonPress:
        case ButtonRelease:
          eMessageBox (_T ("Button Event"));
          break;
        case KeyPress:
        case KeyRelease:
          eMessageBox (_T ("Key Event"));
          break;
        case MotionNotify:
          eMessageBox (_T ("Motion Notify Event"));
          break;
        case ResizeRequest:
          eMessageBox (_T ("Resize Request Event"));
          break;
        case FocusIn:
        case FocusOut:
          eMessageBox (_T ("Focus Change Event"));
          break;
        default:
          eMessageBox (_T ("Unknown Event"));
          break;
      };
    } else {
      return;
    }
  }
}




void
eWindowX11::move (const int& left, const int& top)
{
  // NOTE: This moves the Window to the specified coords (the client region may
  //       be offset by a couple of pixels depending on the window style.
//  if (initialized)
//    SetWindowPos (hWndRender, NULL, left, top, 0, 0, SWP_NOSIZE);
}

void
eWindowX11::move_client (const int& left, const int& top)
{
  // Compensate for window borders, and move the client rect to the specified
  // position.
  if (initialized) {
    update_rect ();

    int x_offset = wndRect.rcWindow.left - wndRect.rcClient.left;
    int y_offset = wndRect.rcWindow.top  - wndRect.rcClient.top;

//    SetWindowPos (hWndRender, NULL, left + x_offset, top + y_offset, 0, 0, SWP_NOSIZE);
  }
}

void
eWindowX11::resize (const int& x,    const int& y)
{
//  if (initialized)
//    SetWindowPos (hWndRender, NULL, 0, 0, x, y, SWP_NOMOVE);
}

void
eWindowX11::resize_client (const int& x,    const int& y)
{
  // Set the window rect (includes borders) to match the current client rect.
  memcpy (&wndRect.rcWindow, &wndRect.rcClient, sizeof (eRect));
  
  // Resize the client rect
  wndRect.rcWindow.right  = wndRect.rcWindow.left + x;
  wndRect.rcWindow.bottom = wndRect.rcWindow.top  + y;

  // Adjust the window rect to compensate for borders
//  AdjustWindowRectEx ((tagRECT *)&wndRect.rcWindow, dwStyle, false, dwExStyle);

  // Calculate the required number of pixels to resize the WINDOW to get the
  // desired CLIENT rect.
  int adj_x = wndRect.rcWindow.right  - wndRect.rcWindow.left;
  int adj_y = wndRect.rcWindow.bottom - wndRect.rcWindow.top;

//  if (initialized)
//    SetWindowPos (hWndRender, NULL, 0, 0, adj_x, adj_y, SWP_NOMOVE);
}

/// TODO
void eWindowX11::show (int)
{
  return;
}

/// TODO
bool eWindowX11::recreate (const eWindowParameters&)
{
  return false;
}


void
eWindowX11::update_rect (void)
{
  // Don't care about invalid update_rect's when the window isn't initialized.
  if (! initialized)
    return;

#if 0
  BOOL bIsWindow = IsWindow (hWndRender);
  Assert (bIsWindow != FALSE, _T ("eWindowWin32::update_rect () called with an invalid window handle!"));

  eSetLastError (EPSILON_ERROR_INVALID_WINDOW_HANDLE);

  if (! bIsWindow)
    return;

  WINDOWINFO info;
  info.cbSize = sizeof (info);
  GetWindowInfo (hWndRender, &info);

  memcpy (&wndRect.rcClient, &info.rcClient, sizeof (wndRect.rcClient));
  memcpy (&wndRect.rcWindow, &info.rcWindow, sizeof (wndRect.rcWindow));

  wndRect.cxWindowBorders = info.cxWindowBorders;
  wndRect.cyWindowBorders = info.cyWindowBorders;
#endif
}


const eWindowHandle
eWindowX11::getNativeWindowHandle (void)
{
  return eWindowHandle ((DWORD)0, window);
}
